package p40_spring与tomcat;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Nio2Protocol;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.io.File;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Map;

/**
 * Spring与Tomcat的整合
 * 在_Tomcat的基础上，将DispatcherServlet注册到Tomcat服务器上，即可完成内置Tomcat的绑定
 * boot的refresh就会完成这个整合
 */
public class _2_SpringTomcat {

    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat");
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

        /**
         * 初始化器里，拿到ioc容器，拿到注册到容器的DispatcherServlet
         * 将该DispatcherServlet注册到tomcat，即可完成spring与tomcat的绑定
         */
        context.addServletContainerInitializer((set, ctx)->{
            WebApplicationContext applicationContext = getContext();
//            DispatcherServlet dispatcherServlet = applicationContext.getBean(DispatcherServlet.class);
//            ctx.addServlet("dispatcherServlet", dispatcherServlet)
//                    .addMapping("/");
            for (ServletRegistrationBean registrationBean : applicationContext.getBeansOfType(ServletRegistrationBean.class).values()) {
                //用ServletRegistrationBean将servlet注册到tomcat的context，这种方式更通用
                registrationBean.onStartup(ctx);
            }
        }, Collections.emptySet());

        tomcat.start();
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);

        /**
         * 以上代码就模拟了spring与tomcat结合的代码
         * 实际上，spring启动，refresh容器时，就会完成上诉步骤
         * 这就是内嵌tomcat的原理
         */
    }

    static WebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(WebConfig.class);
        context.refresh();
        return context;
    }

    @Configuration
    static class WebConfig {
        @Bean
        public DispatcherServlet dispatcherServlet(WebApplicationContext context) {
            return new DispatcherServlet(context);
        }
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
            handlerAdapter.setMessageConverters(ListUtil.of(new MappingJackson2HttpMessageConverter()));
            return handlerAdapter;
        }

        @RestController
        static class MyController {
            @GetMapping("/test")
            public Map<String, String> test() {
                return MapUtil.of("hello", "word!!!");
            }
        }
    }

}
